// (C) 2022 Intel Corporation. All rights reserved.
// Your use of Intel Corporation's design tools, logic functions and other 
// software and tools, and its AMPP partner logic functions, and any output 
// files from any of the foregoing (including device programming or simulation 
// files), and any associated documentation or information are expressly subject 
// to the terms and conditions of the Intel Program License Subscription 
// Agreement, Intel FPGA IP License Agreement, or other applicable 
// license agreement, including, without limitation, that your use is for the 
// sole purpose of programming logic devices manufactured by Intel and sold by 
// Intel or its authorized distributors.  Please refer to the applicable 
// agreement for further details.

module lvds_mm_slave
    #(
        parameter  DATA_WIDTH    = 32,
        parameter  ADDR_WIDTH    = 16
    ) 
(
    input                            clk,
    input                            reset,

    //Slave Agent interface, Only exist on master side, connected to real AVMM master device
    input      [ADDR_WIDTH-1:0]      slv_agent_address,
    input                            slv_agent_read,
    input                            slv_agent_write,
    input      [DATA_WIDTH-1:0]      slv_agent_writedata,
    input      [(DATA_WIDTH/8)-1:0]  slv_agent_byteenable,
    output reg [DATA_WIDTH-1:0]      slv_agent_readdata,
    output reg                       slv_agent_readdatavalid,
    output reg                       slv_agent_waitrequest,
    
    //memory interface connected to lvds_mm_ctrl module
    output reg                       mem_write_req,
    output reg                       mem_read_req,
    input                            mem_write_cmpl,
    output reg  [ADDR_WIDTH-1:0]     mem_address,
    output reg  [DATA_WIDTH-1:0]     mem_writedata,
    input       [DATA_WIDTH-1:0]     mem_readdata,
    input                            mem_readdatavalid,
    
    //Sideband Signals connect to lvds_mm_ctrl module
    input       [3:0]                status_reg,
	 input                            avmm_timeout,
    input                            crc_check_pass,     //1'b1: Pass; 1'b0: Fail.
    input                            unexpected_cmpl
);

    localparam IDLE          = 8'h00;
    localparam WAIT_ACK      = 8'h01;
    localparam WR_WAIT_CMPL  = 8'h02;
    localparam RD_WAIT_VALID = 8'h04;
    localparam RD_HOLD       = 8'h08;

reg [ 7:0] state;

// Control State Machine
always @ (posedge clk) begin
    if (reset) begin        
        slv_agent_readdata      <=  'h0;
        slv_agent_readdatavalid <=  'h0;
        slv_agent_waitrequest   <=  'h0; //Don't waitrequest I2C_Slave in reset status
        mem_write_req           <=  'h0;
        mem_read_req            <=  'h0;
        mem_address             <=  'h0;
        mem_writedata           <=  'h0;        
        state                   <= IDLE;
    end else begin
        case (state)
            IDLE: begin
                slv_agent_readdata      <=  'h0;
                slv_agent_readdatavalid <=  'h0;
                slv_agent_waitrequest   <=  'h0;
                mem_write_req           <=  'h0;
                mem_read_req            <=  'h0;
                mem_address             <=  'h0;
                mem_writedata           <=  'h0;
                
                if (slv_agent_read || slv_agent_write) begin
                    mem_write_req         <= slv_agent_write;
                    mem_read_req          <= slv_agent_read;
                    mem_address           <= slv_agent_address;
                    mem_writedata         <= slv_agent_writedata;   
                    slv_agent_waitrequest <= 1'h1;  //assert wait request immediately after read/write asserts
                    state                 <= WAIT_ACK;
                end
            end
            
            //There are three kinds of error while waitting for ACK from remote end
            //1. timeout
            //2. crc fail
            //3. unexpedted completion: get read ack while issuing a write command
            WAIT_ACK: begin
                slv_agent_waitrequest   <= 1'h1; // waitrequest I2C_Slave to block next I2C command    
            
                if (mem_write_req) begin
                    mem_write_req <= 1'b0;
                    state         <= WR_WAIT_CMPL;
                end else begin
                    mem_read_req  <= 1'b0;
                    state         <= RD_WAIT_VALID;
                end
            end
            
            WR_WAIT_CMPL: begin //wait here until write completion or error from remote end received.
                slv_agent_waitrequest   <= 1'b1;
                
                if( mem_write_cmpl || (|status_reg) ) begin
                    slv_agent_waitrequest   <= 1'b0;
                    state                   <= IDLE;
                end
            end
            
            RD_WAIT_VALID: begin
                slv_agent_waitrequest   <= 1'b1;
                
                if (mem_readdatavalid) begin
                    slv_agent_waitrequest   <= 1'b0;
                    slv_agent_readdata      <= mem_readdata;
                    slv_agent_readdatavalid <= 1'b1;
                    state                   <= IDLE;
                end else if ( |status_reg ) begin             //in error condition, back all 1's data to BMC and log in status_reg
                    slv_agent_waitrequest   <= 1'b0;
						  slv_agent_readdata      <= 32'hFFFF_FFFF;
                    slv_agent_readdatavalid <= 1'b1;
                    state                   <= IDLE;
                end             
            end                        
            
            default: begin
                slv_agent_readdata      <=  'h0;
                slv_agent_readdatavalid <=  'h0;
                slv_agent_waitrequest   <=  'h0;
                mem_write_req           <=  'h0;
                mem_read_req            <=  'h0;
                mem_address             <=  'h0;
                mem_writedata           <=  'h0;
                state                   <= IDLE;               
            end
        endcase        
    end
end

endmodule    